<template>
	<view class="content" :style="{ height: windowHeight + 'px' }">
		<!-- 聊天内容区域 -->
		<scroll-view class="chat-container" scroll-y :scroll-into-view="scrollToView">
			<view class="message-list">
				<view v-for="(item, index) in messageList" :key="index" :id="'item-' + index"
					:class="['message-item', item.type === 'user' ? 'message-right' : 'message-left']">
					<image class="avatar" :src="item.type === 'user' ? userAvatar : aiAvatar"></image>
					<view class="message-content">
						<view class="message-content-items">
							<view class="laba_div">
								<image class="laba" @click="labaClick(item.content || '', item)"
									v-if="!item.isPlay && item.type === 'ai'"
									src="https://dujiaojing.oss-cn-huhehaote.aliyuncs.com/uploads/2025/7/29/1753776125617_e66f11aa/bofang.png?x-oss-process=image/resize,w_300"
									mode="" />
								<image class="laba" @click="zantingaClick(item)"
									v-if="item.isPlay && item.type === 'ai'"
									src="https://dujiaojing.oss-cn-huhehaote.aliyuncs.com/uploads/2025/7/29/1753776196939_06b6089f/zanting.png?x-oss-process=image/resize,w_300"
									mode="" />
							</view>
							<text>
								{{ item.content }}
							</text>
						</view>
					</view>
				</view>
			</view>
		</scroll-view>

		<!-- 录音提示蒙层 -->
		<view class="recording-modal" v-if="showRecordingModal">
			<view class="recording-content" :class="{ 'recording-cancel': cancelRecording }">
				<view class="recording-icon">
					<view class="recording-wave" v-if="!cancelRecording"></view>
					<view :class="['recording-image', cancelRecording ? 'icon-cancel' : 'icon-mic']"></view>
				</view>
				<text class="recording-tips">{{ cancelRecording ? '松开手指，取消发送' : '手指上滑，取消发送' }}</text>
				<text class="recording-time">{{ recordingTime }}s</text>
			</view>
		</view>

		<!-- 底部输入区域 -->
		<view class="input-container">
			<!-- <image class="speak"
				:src="'https://dujiaojing.oss-cn-huhehaote.aliyuncs.com/uploads/2025/7/4/1751605501235.png?x-oss-process=image/resize,w_300'"
				@touchstart="startRecording" @touchmove="moveRecording" @touchend="stopRecording"
				@touchcancel="cancelRecord"></image> -->
			<image
				src="https://dujiaojing.oss-cn-huhehaote.aliyuncs.com/uploads/2025/7/11/1752222700384.png?x-oss-process=image/resize,w_300"
				mode="" class="speak" @click="speckFlag = true" v-if="!speckFlag"></image>
			<image
				src="https://dujiaojing.oss-cn-huhehaote.aliyuncs.com/uploads/2025/7/11/1752222718984.png?x-oss-process=image/resize,w_300"
				mode="" class="speak" @click="speckFlag = false" v-if="speckFlag"></image>
			<input class="message-input" v-model="inputMessage" placeholder="请输入问题" @confirm="sendMessage"
				v-if="!speckFlag" />
			<view class="enters" v-if="speckFlag" @touchstart="startRecording" @touchmove="moveRecording"
				@touchend="stopRecording" @touchcancel="cancelRecord">
				按住 说话
			</view>
			<button class="send-btn" :disabled="loading" :class="{ 'send-btn-disabled': loading }" @tap="sendMessage">
				{{ loading ? '思考中...' : '提问' }}
			</button>
		</view>
	</view>
</template>

<script>
	import {
		systemInfo
	} from '../../../utils/mixin'
	import config from '@/config/config.js'
	import crypto from 'crypto-js';
	import {
		Base64
	} from 'js-base64';
	import {
		callWithMessage,
		getAliTtsSts,
		getAliOssSts
	} from '@/api/index.js'

	export default {
		components: {},
		mixins: [systemInfo],
		data() {
			return {
				messageList: [{
					type: 'ai',
					content: '您好，我是小鲸AI，有什么科普知识不懂的，可以问我啊，我会思考给你回答问题。'
				}],
				inputMessage: '',
				userAvatar: 'https://dujiaojing.oss-cn-huhehaote.aliyuncs.com/uploads/2025/7/29/1753766505656_ae368fe6/touxiang.png?x-oss-process=image/resize,w_300',
				aiAvatar: '../../../static/img/tabbar/ai.png',
				scrollToView: '',
				loading: false,
				isRecording: false,
				recordingTime: 0,
				recordingTimer: null,
				showRecordingModal: false,
				touchStartY: 0,
				cancelRecording: false,
				recorderManager: null,


				isPlay: false,
				// tts播放实例
				ttsAudio: null,

				speckFlag: false
			};
		},
		onLoad() {
			this.getSystemInfo();
			this.initRecorder();
		},
		onUnload() {
			if (this.ttsAudio) {
				this.ttsAudio.stop();
				this.ttsAudio.destroy();
				this.ttsAudio = null;
			}
			// 重置所有项目的播放状态
			this.messageList.forEach(item => item.isPlay = false);
			this.isPlay = false;
		},
		methods: {
			// 暂停播放
			zantingaClick(item) {
				if (this.ttsAudio) {
					this.ttsAudio.stop();
					item.isPlay = false;
					// 确保状态同步
					this.isPlay = false;
				}
			},
			// 喇叭播放点击
			async labaClick(text, item) {
				// 如果已经有音频在播放，先停止
				if (this.ttsAudio) {
					this.ttsAudio.stop();
					this.ttsAudio.destroy();
					this.ttsAudio = null;
					// 重置所有项目的播放状态
					this.productList.forEach(i => this.$set(i, 'isPlay', false));
					this.isPlay = false;
				}

				try {
					const res = await getAliTtsSts();
					uni.request({
						url: 'https://nls-gateway-cn-shanghai.aliyuncs.com/stream/v1/tts',
						method: 'POST',
						header: {
							"Content-Type": "application/json"
						},
						data: {
							appkey: 's0aO8v0e4EFaPCv0',
							token: res.data.token,
							text: text,
							format: 'mp3',
							sample_rate: 16000,
							volume: 100
						},
						responseType: 'arraybuffer',
						success: async (ttsRes) => {
							// 创建新的音频实例
							const audio = uni.createInnerAudioContext();

							// #ifdef MP-WEIXIN
							audio.obeyMuteSwitch = false;
							uni.setInnerAudioOption({
								obeyMuteSwitch: false
							});
							const ttsPath = `${wx.env.USER_DATA_PATH}/tts${new Date().getTime()}.mp3`;
							// #endif

							// #ifdef APP-PLUS
							const ttsPath = `_doc/tts${new Date().getTime()}.mp3`;
							// #endif

							this.ttsAudio = audio;

							// 设置音频事件
							audio.onPlay(() => {
								console.log('音频开始播放');
								item.isPlay = true;
								this.isPlay = true;
							});

							audio.onStop(() => {
								console.log('音频停止播放');
								item.isPlay = false;
								this.isPlay = false;
							});

							audio.onEnded(() => {
								console.log('音频播放结束');
								item.isPlay = false;
								this.isPlay = false;
								// 清理临时文件
								try {
									// #ifndef APP-PLUS
									const fs = uni.getFileSystemManager();
									fs.unlinkSync(ttsPath);
									// #endif
									// #ifdef APP-PLUS
									plus.io.resolveLocalFileSystemURL(ttsPath, (entry) => {
										entry.remove(() => {
											console.log('文件删除成功');
										}, (e) => {
											console.error('文件删除失败', e);
										});
									});
									// #endif
								} catch (e) {
									console.error(e);
								}
								// 销毁音频实例
								audio.destroy();
								this.ttsAudio = null;
							});

							audio.onError((res) => {
								console.error('音频播放出错', res);
								item.isPlay = false;
								this.isPlay = false;
								// 清理资源
								try {
									// #ifndef APP-PLUS
									const fs = uni.getFileSystemManager();
									fs.unlinkSync(ttsPath);
									// #endif
									// #ifdef APP-PLUS
									plus.io.resolveLocalFileSystemURL(ttsPath, (entry) => {
										entry.remove(() => {
											console.log('文件删除成功');
										}, (e) => {
											console.error('文件删除失败', e);
										});
									});
									// #endif
								} catch (e) {
									console.error(e);
								}
								audio.destroy();
								this.ttsAudio = null;
							});

							// 写入文件并播放
							// #ifdef APP-PLUS
							try {
								const ttsPath = `tts_${Date.now()}.mp3`;

								// 写入文件
								const success = await this.writeFileToApp(ttsPath, ttsRes.data);
								if (success) {
									const fullPath = `_doc/${ttsPath}`;
									audio.src = fullPath;

									// 确保音频实例已正确初始化
									setTimeout(() => {
										audio.play()
										this.$set(item, 'isPlay', true); // 确保播放成功后更新
									}, 100);
								}
							} catch (e) {
								console.error('写入或播放失败:', e);
								this.cleanupResources(audio);
							}
							// #endif

							// #ifndef APP-PLUS
							// 小程序端使用 uni.getFileSystemManager()
							const fs = uni.getFileSystemManager();
							fs.writeFile({
								filePath: ttsPath,
								data: ttsRes.data,
								encoding: 'binary',
								success: () => {
									audio.src = ttsPath;
									setTimeout(() => {
										audio.play().catch(err => {
											console.error('播放失败', err);
											if (this.ttsAudio) {
												this.ttsAudio.destroy();
												this.ttsAudio = null;
											}
										});
									}, 100);
								},
								fail: (err) => {
									console.error('写入文件失败', err);
									audio.destroy();
									this.ttsAudio = null;
								}
							});
							// #endif
						},
						fail: (err) => {
							console.error('请求TTS失败', err);
							item.isPlay = false;
							this.isPlay = false;
						}
					});
				} catch (error) {
					console.error('获取TTS STS失败', error);
					item.isPlay = false;
					this.isPlay = false;
				}
			},

			// 将 ArrayBuffer 写入 APP 平台文件
			writeFileToApp(fileName, buffer) {
				return new Promise((resolve, reject) => {
					plus.io.resolveLocalFileSystemURL(
						"_doc/",
						(entry) => {
							entry.getFile(
								fileName, {
									create: true
								},
								(fileEntry) => {
									fileEntry.createWriter(
										(writer) => {
											writer.onwrite = () => {
												resolve(true)
											};
											writer.onerror = (err) => {
												console.error("写入错误详情:", err); // 打印错误对象
												reject(err);
											};
											const bases = uni.arrayBufferToBase64(buffer)
											writer.writeAsBinary(bases); // 确保传入 ArrayBuffer
										},
										(err) => {
											console.error("创建 Writer 失败:", err);
											reject(err);
										}
									);
								},
								(err) => {
									console.error("获取文件失败:", err);
									reject(err);
								}
							);
						},
						(err) => {
							console.error("解析文件系统失败:", err);
							reject(err);
						}
					);
				});
			},
			async GetAliOssSts() {
				const res = await getAliOssSts()
				return res.data
			},
			initRecorder() {
				console.log(1)
				this.recorderManager = uni.getRecorderManager();
				console.log(2)
				this.recorderManager.onStop(async (res) => {
					console.log(3)
					if (!this.cancelRecording && this.recordingTime >= 1) {
						console.log(4)
						// TODO: 处理录音文件
						const response = await getAliTtsSts();
						console.log(5)
						// 上传至oss
						const {
							accessKeyId,
							accessKeySecret,
							securityToken
						} = await this.GetAliOssSts()
						console.log(6)
						this.uploadFilePromise(res.tempFilePath, {
							accessKeyId,
							accessKeySecret,
							securityToken
						}, (url, ind) => {
							console.log(7)
							const urls = config.pic_prefix + url
							console.log(8)
							uni.request({
								url: 'https://nls-gateway-cn-shanghai.aliyuncs.com/stream/v1/FlashRecognizer?token=' +
									response.data.token +
									'&format=mp3&appkey=zaMWoA2fvwMmuJic&audio_address=' +
									urls,
								method: 'POST',
								header: {
									"Content-Type": "application/json"
								},
								data: {
									appkey: 'zaMWoA2fvwMmuJic',
									token: response.data.token,
									format: 'mp3',
									audio_address: urls
								},
								success: async (ttsRes) => {
									if (ttsRes.data.status == 20000000) {
										const dataList = ttsRes.data.flash_result.sentences
											.map(item => item.text)
										this.loading = true;
										this.messageList.push({
											type: 'user',
											content: dataList.join('')
										});
										this.scrollToBottom();

										const res = await callWithMessage({
											question: dataList.join('')
										});
										this.messageList.push({
											type: 'ai',
											content: res.data.output.choices[0]
												.message.content
										});
										this.scrollToBottom();
										this.loading = false;
									} else {
										uni.$u.toast('未识别到语音')
									}

								},
								fail: (err) => {
									console.error('请求TTS失败', err);
									item.isPlay = false;
									this.isPlay = false;
								}
							});

						})

					}
				});
			},

			// 上传
			uploadFilePromise(url, {
				accessKeyId,
				accessKeySecret,
				securityToken
			}, fn) {
				let _that = this
				let fileName = `${new Date().getTime()}.${url.split('.').pop()}`
				const date = new Date();
				const year = date.getFullYear();
				const month = date.getMonth() + 1;
				const dateR = date.getDate();
				date.setHours(date.getHours() + 1)
				const policy = Base64.encode(JSON.stringify({
					expiration: date.toISOString(), // 设置policy过期时间。
					conditions: [
						// 限制上传大小。
						["content-length-range", 0, 30 * 1024 * 1024],
					],
				}))
				uni.uploadFile({
					url: config.pic_prefix,
					filePath: url,
					name: 'file', // 必须填file。
					formData: {
						signature: crypto.enc.Base64.stringify(crypto.HmacSHA1(policy, accessKeySecret)),
						OSSAccessKeyId: accessKeyId,
						policy,
						key: `uploads/${year}/${month}/${dateR}/${fileName}`,
						'x-oss-security-token': securityToken // 使用STS签名时必传。
					},
					success: (data) => {
						// 处理路径，返回的路径是http的，将http干掉，跟之前的url拼接一下，已逗号的形式拼接，然后存入fileList中
						fn(`uploads/${year}/${month}/${dateR}/${fileName}`)
					},
				});
			},

			scrollToBottom() {
				this.scrollToView = 'item-' + (this.messageList.length - 1)
			},
			async sendMessage() {
				if (!this.inputMessage.trim()) return;
				this.loading = true;
				this.messageList.push({
					type: 'user',
					content: this.inputMessage
				});
				this.scrollToBottom();

				const res = await callWithMessage({
					question: this.inputMessage
				});
				this.messageList.push({
					type: 'ai',
					content: res.data.output.choices[0].message.content
				});
				this.scrollToBottom();
				this.loading = false;
				this.inputMessage = '';
			},
			async startRecording(e) {
				this.touchStartY = e.touches[0].clientY;
				this.isRecording = true;
				this.showRecordingModal = true;
				this.cancelRecording = false;
				this.recordingTime = 0;

				this.recordingTimer = setInterval(() => {
					this.recordingTime++;
					if (this.recordingTime >= 60) {
						this.stopRecording();
					}
				}, 1000);

				const that = this

				this.recorderManager.start({
					duration: 60000,
					sampleRate: 16000,
					numberOfChannels: 1,
					encodeBitRate: 48000,
					format: 'mp3'
				});


			},
			moveRecording(e) {
				if (!this.isRecording) return;
				const touchY = e.touches[0].clientY;
				const moveDistance = this.touchStartY - touchY;
				this.cancelRecording = moveDistance > 100;
			},
			stopRecording() {
				if (!this.isRecording) return;

				clearInterval(this.recordingTimer);
				this.isRecording = false;
				this.showRecordingModal = false;
				this.recorderManager.stop();
			},
			cancelRecord() {
				this.cancelRecording = true;
				this.stopRecording();
			}
		}
	};
</script>

<style lang="less" scoped>
	.content {
		width: 100%;
		box-sizing: border-box;
		display: flex;
		flex-direction: column;

		.chat-container {
			box-sizing: border-box;
			padding: 20rpx;
			height: calc(100% - 180rpx);
			// flex: 1;
			overflow: auto;

			.message-list {
				.message-item {
					display: flex;
					margin-bottom: 30rpx;

					.avatar {
						width: 80rpx;
						height: 80rpx;
						border-radius: 50%;
					}

					.message-content {
						max-width: 70%;
						margin: 0 20rpx;
						padding: 20rpx;
						border-radius: 10rpx;
						background-color: #f0f0f0;
						word-break: break-all;

						.laba_div {
							display: inline-block;

							.laba {
								width: 50rpx;
								height: 50rpx;
								position: relative;
								top: 8rpx;
							}
						}
					}
				}

				.message-left {
					.message-content {
						background-color: #f0f0f0;
					}
				}

				.message-right {
					flex-direction: row-reverse;

					.message-content {
						background-color: #007AFF;
						color: #fff;
					}
				}
			}
		}

		.recording-modal {
			position: fixed;
			top: 0;
			left: 0;
			right: 0;
			bottom: 0;
			background-color: rgba(0, 0, 0, 0.3);
			display: flex;
			justify-content: center;
			align-items: center;
			z-index: 999;

			.recording-content {
				width: 300rpx;
				height: 300rpx;
				background-color: rgba(0, 0, 0, 0.7);
				border-radius: 20rpx;
				display: flex;
				flex-direction: column;
				align-items: center;
				justify-content: center;

				&.recording-cancel {
					background-color: rgba(255, 0, 0, 0.7);
				}

				.recording-icon {
					position: relative;
					width: 120rpx;
					height: 120rpx;
					margin-bottom: 20rpx;

					.recording-wave {
						position: absolute;
						top: 50%;
						left: 50%;
						transform: translate(-50%, -50%);
						width: 120rpx;
						height: 120rpx;
						border-radius: 50%;
						background-color: rgba(255, 255, 255, 0.2);
						animation: wave 1.5s infinite;
					}

					.recording-icon {
						// ... 现有样式 ...

						.recording-image {
							position: relative;
							width: 80rpx;
							height: 80rpx;
							top: 20rpx;

							&.icon-mic {
								&::before {
									content: '';
									position: absolute;
									width: 24rpx;
									height: 40rpx;
									border-radius: 12rpx;
									background: #fff;
									left: 28rpx;
									top: 20rpx;
								}

								&::after {
									content: '';
									position: absolute;
									width: 40rpx;
									height: 40rpx;
									border: 6rpx solid #fff;
									border-radius: 50%;
									left: 17rpx;
									top: 10rpx;
								}
							}

							&.icon-cancel {

								&::before,
								&::after {
									content: '';
									position: absolute;
									width: 50rpx;
									height: 6rpx;
									background: #fff;
									left: 15rpx;
									top: 37rpx;
								}

								&::before {
									transform: rotate(45deg);
								}

								&::after {
									transform: rotate(-45deg);
								}
							}
						}
					}
				}

				.recording-tips {
					color: #fff;
					font-size: 28rpx;
					margin-bottom: 10rpx;
				}

				.recording-time {
					color: #fff;
					font-size: 24rpx;
				}
			}
		}

		.input-container {
			position: fixed;
			bottom: 0;
			left: 0;
			right: 0;
			padding: 30rpx 20rpx 50rpx;
			background-color: #fff;
			display: flex;
			align-items: center;
			border-top: 1rpx solid #eee;
			z-index: 999;

			.speak {
				width: 60rpx;
				height: 60rpx;
				margin-right: 10rpx;
			}

			.message-input {
				flex: 1;
				height: 80rpx;
				background-color: #f5f5f5;
				border-radius: 40rpx;
				padding: 0 30rpx;
				margin-right: 20rpx;

				&[disabled] {
					opacity: 0.7;
				}
			}

			.enters {
				flex: 1;
				height: 80rpx;
				background-color: #f5f5f5;
				border-radius: 40rpx;
				padding: 0 30rpx;
				margin-right: 20rpx;
				display: flex;
				align-items: center;
				justify-content: center;
			}

			.send-btn {
				width: 160rpx;
				height: 80rpx;
				line-height: 80rpx;
				text-align: center;
				background-color: #007AFF;
				color: #fff;
				border-radius: 40rpx;
				font-size: 28rpx;

				&.send-btn-disabled {
					background-color: #999;
					opacity: 0.7;
				}
			}
		}

		@keyframes wave {
			0% {
				transform: translate(-50%, -50%) scale(1);
				opacity: 1;
			}

			100% {
				transform: translate(-50%, -50%) scale(2);
				opacity: 0;
			}
		}
	}
</style>